package com.etone.smartAudit.service;

import cn.hutool.core.io.resource.ResourceUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.baomidou.mybatisplus.extension.api.R;
import com.etone.smartAudit.apiIntercept.Result;
import com.etone.smartAudit.domain.CoordDistance;
import com.etone.smartAudit.dto.gis.MiniDistanceDTO;
import com.etone.smartAudit.enums.LonLatType;
import com.etone.smartAudit.error.CustomParameterizedException;
import com.etone.smartAudit.utils.Excel.ComplexHeadStyles;
import com.etone.smartAudit.utils.Excel.ExcelShell;
import com.etone.smartAudit.utils.Excel.ExcelUtil;
import com.etone.smartAudit.utils.Excel.MyEasyExcelUtils;
import com.etone.smartAudit.utils.gis.GisUtil;
import com.etone.smartAudit.utils.gis.Gps;
import com.etone.smartAudit.utils.gis.LonLatConvert;
import com.etone.smartAudit.utils.gis.PointInPoly;
import com.etone.smartAudit.vo.workOrder.ResponseVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.stream.Collectors;

/**
 * @author 张任豪
 */
@Service
@AllArgsConstructor
@Slf4j
public class GisServiceService {

    private FileUploadService fileUploadService;


    public String lonLatConvert(Double fLat, Double fLon, Integer srcLonLat, Integer tgtLonLat, Integer returnType) {
        String evaluate = GisUtil.lonLatConvert(fLat, fLon, srcLonLat, tgtLonLat, returnType);
        if (StringUtils.isEmpty(evaluate)) {
            return "经纬度转换异常";
        }
        return evaluate;
    }

    public ResponseVO<String> pointInPoly(Double fLat, Double fLon, List<Gps> gpsList) {
        if (gpsList.size() < 3) {
            return ResponseVO.fail("经纬度集合小于三个点，无法形成一个面");
        }
        PointInPoly poly = new PointInPoly();
        String polygon = "";
        for (int i = 0; i < gpsList.size(); i++) {
            polygon += gpsList.get(i).getWgLon() + "," + gpsList.get(i).getWgLat() + ";";
        }
        String evaluate = poly.evaluate(fLon, fLat, polygon);
        if ("1".equals(evaluate)) {
            return ResponseVO.success("该坐标在该平面中");
        } else if ("0".equals(evaluate)) {
            return ResponseVO.success("该坐标在该平面上");
        } else {
            return ResponseVO.fail("坐标在平面外");
        }
    }

    public ResponseVO<Double> distance(Double lon1, Double lat1, Double lon2, Double lat2) {
        double distance = GisUtil.distance(lon1, lat1, lon2, lat2);
        return new ResponseVO<>("success", "200", distance);
    }

    public void distanceTemplate(HttpServletResponse response) {
        String path = "tfs/coord_distance.xlsx";
        InputStream inputStream = ResourceUtil.getStream(path);
        fileUploadService.getFile(inputStream,response,"经纬度距离测算模板.xlsx");
    }

    public void distanceCalculate(MultipartFile file, HttpServletResponse response) {
        List<CoordDistance> coords = new ArrayList<>();
        try{
            List<ExcelShell> shells = ExcelUtil.read(file);
            ExcelShell excelShell = shells.get(0);
            List<List<Object>> data = excelShell.getContents();
            for (List<Object> content : data) {
                CoordDistance coordDistance = new CoordDistance();
                coordDistance.setLon1(Double.parseDouble(content.get(0).toString()));
                coordDistance.setLat1(Double.parseDouble(content.get(1).toString()));
                coordDistance.setLon2(Double.parseDouble(content.get(2).toString()));
                coordDistance.setLat2(Double.parseDouble(content.get(3).toString()));
                coords.add(coordDistance);
            }
        }catch (Exception e){
            throw new CustomParameterizedException("文件解析失败");
        }

        //批量计算坐标距离
        List<CoordDistance> distanceList = GisUtil.countDistances(coords);
        //导出数据
        List<String> hears = Arrays.asList("经度1", "纬度1", "经度2", "纬度2", "经纬度距离(单位米)");

        List<List<String>> contents = distanceList.stream().map(vo ->
                Arrays.asList(String.valueOf(vo.getLon1()),String.valueOf(vo.getLat1()),String.valueOf(vo.getLon2())
                        ,String.valueOf(vo.getLat2()),String.valueOf(vo.getDistance()))
        ).collect(Collectors.toList());
        ExcelUtil.export("经纬度距离测算结果",hears,contents,response);
    }

    public void lonLatTemplate(HttpServletResponse response) {
        String path = "tfs/lonLatConvert_template.xlsx";
        InputStream inputStream = ResourceUtil.getStream(path);
        fileUploadService.getFile(inputStream,response,"经纬度转换模板.xlsx");
    }

    public void batchLonLat(MultipartFile file, HttpServletResponse response) {
        List<LonLatConvert> lonLatConverts = new ArrayList<>();
        try {
            List<ExcelShell> shells = ExcelUtil.read(file);
            ExcelShell excelShell = shells.get(0);
            List<List<Object>> data = excelShell.getContents();
            //读取Excel表格
            for (List<Object> lonLat : data) {
                LonLatConvert lonLatConvert = new LonLatConvert();
                lonLatConvert.setLon(Double.parseDouble(lonLat.get(0).toString()));
                lonLatConvert.setLat(Double.parseDouble(lonLat.get(1).toString()));
                lonLatConvert.setSrcLonLat(Double.valueOf(lonLat.get(2).toString()).intValue());
                lonLatConvert.setTgtLonLat(Double.valueOf(lonLat.get(3).toString()).intValue());
                lonLatConverts.add(lonLatConvert);
            }
        }catch (Exception e){
            throw new CustomParameterizedException("文件解析失败");
        }

        //批量转换坐标
        List<LonLatConvert> results = GisUtil.batchLonLat(lonLatConverts);
        //生成表格
        List<String> hears = Arrays.asList("经度", "纬度", " 源经纬度", "目标经纬度", "转换结果:");

        List<List<String>> contents = results.stream().map(vo ->
                Arrays.asList(String.valueOf(vo.getLon()),String.valueOf(vo.getLat()),LonLatType.getRemark(vo.getSrcLonLat())
                        ,LonLatType.getRemark(vo.getTgtLonLat()),vo.getResult())).collect(Collectors.toList());
        ExcelUtil.export("经纬度转换",hears,contents,response);

    }


    /**
     * 批量经纬度坐标转换
     * @param lonLatConverts
     * @return
     */
    public Result lonLatConverts(List<LonLatConvert> lonLatConverts) {
        List<LonLatConvert> lonLatConvertList = new ArrayList<>();
        try {
            lonLatConvertList = GisUtil.batchLonLat(lonLatConverts);
            return Result.success(lonLatConvertList);
        }catch (Exception e){
            return new Result(Result.FAIL_CODE,"调用参数异常",lonLatConvertList);
        }
    }

    /**
     * 批量坐标距离测算
     * @param distances
     * @return
     */
    public Result distanceCalculates(List<CoordDistance> distances) {
        List<CoordDistance> coordDistances = new ArrayList<>();
        try{
            coordDistances = GisUtil.countDistances(distances);
            return Result.success(coordDistances);
        }catch (Exception e){
            return new Result(Result.FAIL_CODE,"调用参数异常",coordDistances);
        }
    }

    public Result miniDistanceTemplate(MultipartFile file, HttpServletResponse response) {
        try {
            List<Gps> gpsList = new ArrayList<>();
            EasyExcel.read(file.getInputStream(), MiniDistanceDTO.class, new PageReadListener<MiniDistanceDTO>(dataList -> {
                for (MiniDistanceDTO dto : dataList) {
                    gpsList.add(Gps.builder().wgLon(dto.getLon()).wgLat(dto.getLat()).build());
                }
            })).sheet().headRowNumber(2).doRead();

            List<MiniDistanceDTO> dtoList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(gpsList)){
                for (Gps gps : gpsList) {
                    CoordDistance coordDistance = GisUtil.singleDistance(gps, gpsList);
                    dtoList.add(MiniDistanceDTO.builder()
                    .lon(coordDistance.getLon1())
                    .lat(coordDistance.getLat1())
                    .mixLon(coordDistance.getLon2())
                    .minLat(coordDistance.getLat2())
                    .mixDistance(coordDistance.getDistance()).build());
                }
            }

            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("计算最短距离结果", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + System.currentTimeMillis() + ".xlsx");

            EasyExcel.write(response.getOutputStream(),MiniDistanceDTO.class)
                    .registerWriteHandler(MyEasyExcelUtils.setHorizontalCellStyleStrategy())
                    .sheet("最短距离").doWrite(dtoList);
            return Result.success("success");
        } catch (Exception e) {
            log.info("计算最短距离模板解析异常：{}",e.getMessage());
            throw new CustomParameterizedException("计算最短距离模板解析异常,请检查格式!");
        }
    }

    public Result countMixDistance(List<Gps> gps) {
        List<CoordDistance> distances = new ArrayList<>();
        if (!CollectionUtils.isEmpty(gps)){
            for (Gps gp : gps) {
                CoordDistance coordDistance = GisUtil.singleDistance(gp, gps);
                distances.add(coordDistance);
            }
        }
        return Result.success(distances);
    }
}
